package org.springblade.common.config;

import com.alibaba.fastjson.JSON;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import io.micrometer.core.instrument.util.IOUtils;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.net.telnet.TelnetClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.modules.monitor.entity.Application;
import org.springblade.modules.monitor.entity.ChainRelationship;
import org.springblade.modules.monitor.entity.ChainSource;
import org.springblade.modules.monitor.mapper.ApplicationMapper;
import org.springblade.modules.monitor.mapper.ChainRelationshipMapper;
import org.springblade.modules.monitor.mapper.ChainSourceMapper;
import org.springblade.modules.monitor.pojo.ChainSourceWithRelationship;
import org.springblade.modules.monitor.service.IApplicationService;
import org.springblade.modules.monitor.service.IChainRelationshipService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * @ClassName ChainRelationshipTask
 * @Description
 * @Author zhang_xfei
 * @DATE 2020 2020/8/18 17:26
 **/
@Component // 此注解必加
@EnableScheduling // 此注解必加
public class ChainRelationshipTask {

	@Autowired
	private ChainRelationshipMapper chainRelationshipMapper;
	@Autowired
	private ChainSourceMapper chainSourceMapper;
	@Autowired
	private IChainRelationshipService chainRelationshipService;
	private static final Logger LOGGER =  LoggerFactory.getLogger(ChainRelationshipTask.class);
	public synchronized String  chainRelationshipScan(){
		List<ChainRelationship> relationships = chainRelationshipMapper.selectList(null);
		List<ChainSource> sources = chainSourceMapper.selectList(null);
		if(CollectionUtils.isEmpty(relationships)||CollectionUtils.isEmpty(sources)){
			return null;
		}else {
			//对relationships 进行分组
			List<ChainSourceWithRelationship> sourceShips = new ArrayList<>();
			sources.forEach(x->{
				ChainSourceWithRelationship sourceShip = new ChainSourceWithRelationship();
				BeanUtil.copy(x, sourceShip);
				List<ChainRelationship> relationshipList = new ArrayList<>();
				relationships.forEach(y->{
					if(x.getId().equals(y.getSourceId())){
						relationshipList.add(y);
					}
				});
				sourceShip.setRelationshipList(relationshipList);
				sourceShips.add(sourceShip);
			});


			for (ChainSourceWithRelationship ship : sourceShips) {
				if(StringUtil.isBlank(ship.getCommand())){
					LOGGER.warn("请注意："+ship.getIp()+"的链路检测，失败判定字符未定义，不作其他处理");
					continue;
				}
				if(ship.getPort().equals(22)){
					checkSSHConnectStatus(ship);
				}else if(ship.getPort().equals(23)){
					//Telnet 端口需进行特殊处理
					checkTelnetConnectStatus(ship);
				}
			}

			List<ChainRelationship> tempRelationships = new ArrayList<>();

			for (ChainSourceWithRelationship ship : sourceShips) {
				tempRelationships.addAll(ship.getRelationshipList());
			}
			chainRelationshipService.batchUpdateStatus(tempRelationships, sources);
		}
		return "SUCCESS";
	}

	public static void checkSSHConnectStatus(ChainSourceWithRelationship ship) {
		JSch jSch = new JSch();
		try {
			LOGGER.info("正在登录："+ship.getIp()+":"+ship.getPort());
			Session session = jSch.getSession(ship.getUsername(), ship.getIp(), ship.getPort());
			session.setConfig("StrictHostKeyChecking", "no");
			session.setPassword(ship.getPwd());
			session.setTimeout(10000);
			session.connect();
			List<ChainRelationship> relationshipList = ship.getRelationshipList();

			relationshipList.forEach(x->{
				try {
					ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
					LOGGER.info("连接已打开："+ship.getIp()+":"+ship.getPort());
					InputStream in = null;
					try {
						in = channelExec.getInputStream();
					} catch (IOException e) {
						LOGGER.error("请注意：获取返回流 "+ship.getIp()+":"+ship.getPort()+"代码执行存在异常",e.getMessage());
						e.printStackTrace();
					}
					String command = ship.getCommand().replace("{{}}",x.getIp());
					channelExec.setCommand(command);
					channelExec.setErrStream(System.err);
					channelExec.connect();
					String out = IOUtils.toString(in, Charset.forName("UTF-8"));
					LOGGER.info(out);
					in.close();
					String[] split = ship.getFailureStr().split(",");
					boolean failureFlag = false;
					if(StringUtil.isNotBlank(out)){
						for (String s : split) {
							if(out.contains(s)){
								failureFlag = true;
								break;
							}
						}
					}
					//存在空值或错误内容
					if(failureFlag){
						LOGGER.error("请注意："+ship.getIp()+"到"+x.getIp()+"的链路检测，返回的数据包为空，将对此链路标记为异常状态");
						x.setStatus(0);
					}else{
						if(x.getStatus().equals(0)){
							LOGGER.info("请注意："+ship.getIp()+"到"+x.getIp()+"的链路检测之前为异常状态,ping 命令接收到返回信息,修改其结果为正常状态");
						}
						x.setStatus(1);
					}

					channelExec.disconnect();
				} catch (JSchException e) {
					LOGGER.error("请注意："+ship.getIp()+"到"+x.getIp()+"的链路检测，代码执行存在异常",e.getMessage());
					e.printStackTrace();
				} catch (Exception ex){
					LOGGER.error("请注意："+ship.getIp()+"到"+x.getIp()+"的链路检测，代码执行存在异常",ex.getMessage());
				}

			});
			session.disconnect();
		} catch (JSchException ej) {
			LOGGER.error("请注意：无法登录到 "+ship.getIp()+":"+ship.getPort()+"代码执行存在异常",ej.getMessage());
			ej.printStackTrace();
		}
	}


	public static void checkTelnetConnectStatus(ChainSourceWithRelationship ship) {
		List<ChainRelationship> relationshipList = ship.getRelationshipList();
		for (ChainRelationship chainRelationship : relationshipList) {
			try {
				TelnetClient telnetClient = new TelnetClient("vt200");  //指明Telnet终端类型，否则会返回来的数据中文会乱码
				telnetClient.setDefaultTimeout(10000); //socket延迟时间：5000ms
				telnetClient.connect(ship.getIp(),23);  //建立一个连接,默认端口是23
				InputStream inputStream = telnetClient.getInputStream(); //读取命令的流
				OutputStream outputStream = telnetClient.getOutputStream();
				PrintStream pStream = new PrintStream(outputStream);  //写命令的流
				byte[] b = new byte[1024];
				int size;
				StringBuffer sBuffer = new StringBuffer();
				while(true) {     //读取Server返回来的数据，直到读到登陆标识，这个时候认为可以输入用户名
					size = inputStream.read(b);
					if(-1 != size) {
						sBuffer.append(new String(b,0,size));
						if(sBuffer.toString().trim().endsWith("Login:")) {
							break;
						}
					}
				}
				//录入用户名
				pStream.println(ship.getUsername()); //写命令
				pStream.flush(); //将命令发送到telnet Server

				while(true) {     //读取Server返回来的数据，直到读到登陆标识，这个时候认为可以输入用户名
					size = inputStream.read(b);
					if(-1 != size) {
						sBuffer.append(new String(b,0,size));
						if(sBuffer.toString().trim().endsWith("Password:")) {
							break;
						}
					}
				}
				//录入密码
				pStream.println(ship.getPwd());
				pStream.flush();

				while(true) {
					size = inputStream.read(b);
					if(-1 != size) {
						sBuffer.append(new String(b,0,size));
						if(sBuffer.toString().trim().endsWith("#")) {
							break;
						}
					}
				}
				//录入命令行
				String command = ship.getCommand().replace("{{}}",chainRelationship.getIp());
				pStream.println(command);
				pStream.flush();

				while(true) {
					size = inputStream.read(b);
					if(-1 != size) {
						sBuffer.append(new String(b,0,size));
						if(sBuffer.toString().trim().endsWith("#")) {
							break;
						}
					}
				}

				LOGGER.info(sBuffer.toString());

				String[] split = ship.getFailureStr().split("\\|\\|\\|");
				LOGGER.info(JSON.toJSONString(split)+"进入判断PING返回结果流程");
				boolean failureFlag = false;
				for (String s : split) {
					if(StringUtil.isNotBlank(sBuffer)&&sBuffer.toString().contains(s)){
						LOGGER.info("命中字符串:"+s);
						failureFlag = true;
						break;
					}
				}
				//存在空值或错误内容
				if(failureFlag){
					LOGGER.error("请注意："+ship.getIp()+"到"+chainRelationship.getIp()+"的链路检测，返回的数据包为空，将对此链路标记为异常状态");
					chainRelationship.setStatus(0);
				}else{
					if(chainRelationship.getStatus().equals(0)){
						LOGGER.info("请注意："+ship.getIp()+"到"+chainRelationship.getIp()+"的链路检测之前为异常状态,ping 命令接收到返回信息,修改其结果为正常状态");
					}
					chainRelationship.setStatus(1);
				}
				LOGGER.info("当前状态"+chainRelationship.getStatus());
				//关闭连接
				telnetClient.disconnect();
			} catch (SocketException e) {
				LOGGER.error("请注意："+ship.getIp()+":"+ship.getPort()+"连接时出现异常",e.getMessage());
				e.printStackTrace();
			} catch (IOException e) {
				LOGGER.error("请注意："+ship.getIp()+":"+ship.getPort()+"连接时出现I/O异常",e.getMessage());
				e.printStackTrace();
			}
		}
	}
}
